#ifndef __M_ASYNCWORKER_H__
#define __M_ASYNCWORKER_H__

#include <mutex>
#include <condition_variable>
#include <thread>
#include <atomic>
#include "buffer.hpp"

/*
    异步工作器类

    1.双缓冲区
    2.互斥锁(保证主线程与副线程交换缓冲区的时候,线程安全,不会一个线程在写入或者读取,另一个线程进行交换)
    3.条件变量
    4.回调函数(外部传入函数,告诉异步工作器该如何处理数据)
    5.安全模式(写入空间不足,就阻塞)或者非安全模式(无限扩容)
*/

namespace log
{
    using Functor = std::function<void(Buffer &buffer)>; // 定义函数类型
    enum class AsyncType                                 // 安全模式或者性能模式
    {
        ASYNC_SAFE,
        ASYNC_UNSAFE
    };
    class AsyncWorker
    {
    public:
        using ptr = std::shared_ptr<AsyncWorker>;

        AsyncWorker(const Functor &cb)
            : _callback(cb), _thread(std::thread(&AsyncWorker::worker, this)) // 让线程去交换缓冲区
            ,_running(true), _type(AsyncType::ASYNC_SAFE) // 默认是安全模式
        {
        }

        ~AsyncWorker()
        {
            stop();
        }

        void stop() // 关闭之前,要将数据永久化
        {
            _running = false;
            _pop_cond.notify_all();
            _thread.join();
        }

        void push(const char* message,size_t len) // 生产者进行输入
        {
            if (!_running)
                return; // 日志器被关掉了

            {
                std::unique_lock<std::mutex> lock(_mutex); // 加锁,因为要对缓冲区进行输入,所以这个时候不能交换缓冲区
                if (_type == AsyncType::ASYNC_SAFE)   // 安全模式,只有满足空间要求才会输入，使得缓冲区里面的push函数不会扩容
                    _push_cond.wait(lock, [&]{ return _task_push.WriteAble() >= len; });//注意retrun
                _task_push.push(message, len); // 安全模式下,程序运行到这里,空间一定是满足的,所以push函数内部不会扩容
                                                                  // 非安全模式下,来到这里可能空间不如,此时push函数内部会扩容
            }
            _pop_cond.notify_all(); // 唤醒全部休眠的进程,这里只有一个
        }
        void worker() // 交换缓冲区,落地方向由回调函数决定
        {
            while (1)
            {
                {
                    std::unique_lock<std::mutex> lock(_mutex); // 申请锁,如果生产者没有输入并且输入缓冲区有数据,就进行交换缓冲区                    
                    if (!_running && _task_push.empty())
                        return; // 当日志器被关闭,并且输入缓冲区没有数据了,就退出了,防止死循环
                    _pop_cond.wait(lock, [&]
                                   { return !_running || !_task_push.empty(); }); // 输入缓冲区有数据或者日志器被关闭,都要被唤醒交换缓冲区
                    _task_push.swap(_task_pop);                                   // 交换缓冲区
                }
                if (_type == AsyncType::ASYNC_SAFE) // 只有在安全模式下,才用唤醒,因为非安全模式下生产者压根就没有进入休眠
                    _push_cond.notify_all();        // 唤醒生产者
                _callback(_task_pop);               // 让回调函数去处理日志消息的落地方式
                _task_pop.resset();                // 初始化
            }
            return;
        }

        void SetUnSafe()
        {
            _type = AsyncType::ASYNC_UNSAFE;
        }

    private:
        Functor _callback; // 回调函数
    private:
        Buffer _task_push;                  // 输入缓冲区
        Buffer _task_pop;                   // 读取缓冲区
        AsyncType _type;                    // 异步日志器的类型
        std::atomic<bool> _running;         // 是否正常运行
        std::mutex _mutex;                  // 锁
        std::condition_variable _push_cond; // 输入的条件变量
        std::condition_variable _pop_cond;  // 输出的条件变量
        std::thread _thread;                // 线程
    };
}

#endif